#pragma once

#include <iostream>
#include <memory>
#include <unordered_map>
#include <mutex>
#include "util.hpp"
#include "user.hpp"
#include "./db/db.hpp"
#include "./log/logger.hpp"
/**
 * 主要实现的是游戏房间管理
 * 一个是单独房间的处理，一个是对所有房间进行管理
 * 单独房间主要完成的是 下棋和聊天功能
 */
#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITH 1
#define CHESS_BLACK 2
typedef enum{GAME_START,GAME_OVER} room_statu;

class room
{
public:
    room(uint64_t room_id,online_user* ou,user_table* ut)
    :_room_id(room_id),_player_count(0),_room_statu(GAME_START)
    ,_online_user(ou),_user_table(ut)
    ,_board(BOARD_ROW, std::vector<int>(BOARD_COL, 0))
    {
        DBG_LOG("%lu号房间创建成功!", _room_id);
    }
    ~room()
    {
        DBG_LOG("%lu号房间摧毁成功!", _room_id);
    }
    uint64_t get_room_id() { return _room_id; }
    room_statu get_room_statu() { return _room_statu; }
    int get_player_count() { return _player_count; }
    void add_white_user(uint64_t uid) { _white_id = uid, _player_count++; }
    void add_black_user(uint64_t uid) { _black_id = uid, _player_count++; }
    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }
    //玩家下棋操作
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value json_resq = req;
        //获取房间用户状态
        if(_online_user->user_in_room(_white_id) == false)
        {
            json_resq["result"] = true;
            json_resq["reason"] = "对方掉线，己方获胜!";
            json_resq["winner"] = (Json::UInt64)_black_id;
            return json_resq;
        }
        if(_online_user->user_in_room(_black_id) == false)
        {
            json_resq["result"] = true;
            json_resq["reason"] = "对方掉线，己方获胜!";
            json_resq["winner"] = (Json::UInt64)_white_id;
            return json_resq;
        }
        //开始处理下棋操作
        //1.先获取用户下棋的位置
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        //2.判断当前位置是否存在棋子
        if(_board[chess_row][chess_col] != 0)
        {
            json_resq["result"] = true;
            json_resq["reason"] = "当前位置以存在棋子";
            return json_resq;
        }
        //3.下棋先获取是黑棋还是白棋
        uint64_t cur_uid = req["uid"].asUInt64();
        uint64_t cur_color = cur_uid == _white_id ? CHESS_WHITH : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_color;
        //4.判断该落子是否造成胜利
        uint64_t winner_id = check_win(chess_row,chess_col,cur_color);
        if(winner_id != 0)
            json_resq["reason"] = "五星连珠,获取胜利！";
        json_resq["optype"] = "put_chess";
        json_resq["result"] = true;
        json_resq["winner"] = (Json::UInt64)winner_id;
        return json_resq;
    }
    //玩家聊天操作
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value json_resq = req;
        if(_online_user->user_in_room(_white_id) == false)
        {
            json_resq["result"] = true;
            json_resq["reason"] = "对方掉线!";
            json_resq["winner"] = (Json::UInt64)_black_id;
            return json_resq;
        }
        if (_online_user->user_in_room(_black_id) == false)
        {
            json_resq["result"] = true;
            json_resq["reason"] = "对方掉线！";
            json_resq["winner"] = (Json::UInt64)_white_id;
            return json_resq;
        }
        std::string msg = req["message"].asString();
        size_t pos = msg.find("垃圾");
        if (pos != std::string::npos)
        {
            json_resq["result"] = false;
            json_resq["reason"] = "聊天内容包含敏感词汇!";
            return json_resq;
        }
        // 发送
        json_resq["result"] = true;
        return json_resq;
    }
    //玩家退出操作
    void handle_exit(uint64_t uid)
    {
        Json::Value json_resq;
        //检测房间状态如果为GAME_START时进行异常退出检测
        if(_room_statu == GAME_START)
        {
            Json::UInt64 winner_id = (Json::UInt64)uid == _white_id ? _black_id : _white_id;
            json_resq["optype"] = "put_chess";
            json_resq["result"] = true;
            json_resq["reason"] = "对方掉线,己方获胜!";
            json_resq["room_id"] = (Json::UInt64)_room_id;
            json_resq["uid"] = (Json::UInt64)uid;
            json_resq["row"] = -1;
            json_resq["col"] = -1;
            json_resq["winner"] = (Json::UInt64)winner_id;
            uint64_t lose_id = winner_id = _white_id ? _black_id : _white_id;
            _user_table->win(winner_id);
            _user_table->lose(lose_id);
            _room_statu = GAME_OVER;
            handle_broad_cast(json_resq);
        }
        _player_count--;
        return;
    }
    //总体请求汇总，房间中的每个请求都先经过该函数，然后进行具体调用
    void handle_request(Json::Value& req)
    {
        Json::Value json_resq;
        uint64_t room_id = req["room_id"].asUInt64();
        if(room_id != _room_id)
        {
            json_resq["optype"] = req["optype"].asString();
            json_resq["result"] = false;
            json_resq["reason"] = "房间号不匹配!";
            return handle_broad_cast(json_resq);
        }
        //根据optype来选择调用函数
        //1.下棋
        if(req["optype"].asString() == "put_chess")
        {
            json_resq = handle_chess(req);
            if(json_resq["winner"].asInt64() != 0)
            {
                uint64_t win_id = json_resq["winner"].asUInt64();
                uint64_t lose_id = win_id == _white_id ? _black_id : _white_id;
                _user_table->win(win_id);
                _user_table->lose(lose_id);
                _room_statu = GAME_OVER;
            }
        }
        //2.聊天
        else if(req["optype"].asString() == "chat")
        {
            json_resq = handle_chat(req);
        }
        else
        {
            json_resq["optype"] = req["optype"].asString();
            json_resq["result"] = false;
            json_resq["reason"] = "未知请求!";
        }
        std::string body;
        util_json::serialize(json_resq,body);
        DBG_LOG("房间-广播动作:%s",body.c_str());
        return handle_broad_cast(json_resq);
    }
    //将广播内容传递给所有房间
    void handle_broad_cast(Json::Value& req)
    {
        std::string body;
        util_json::serialize(req,body);
        wsserver_t::connection_ptr wcp = _online_user->get_conn_in_room(_white_id);
        if(wcp.get() != nullptr)
            wcp->send(body);
        else DBG_LOG("白棋选手未连接!");
        wsserver_t::connection_ptr bcp = _online_user->get_conn_in_room(_black_id);
        if(bcp.get() != nullptr)
            bcp->send(body);
        else DBG_LOG("黑棋选手未连接!");
        return;
    }
private:
    uint64_t check_win(int row, int col, int color)
    {
        if (five(row, col, 0, 1, color) ||
            five(row, col, 1, 0, color) ||
            five(row, col, -1, 1, color) ||
            five(row, col, -1, -1, color))
                return color == CHESS_BLACK ? _black_id : _white_id;
        return 0;
    }
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        // row和col是下棋位置， row_off和col_off是偏移量，也是⽅向
        int count = 1;
        int search_row = row + row_off;
        int search_col = col + col_off;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            // 同⾊棋⼦数量++
            count++;
            // 检索位置继续向后偏移
            search_row += row_off;
            search_col += col_off;
        }
        // 反方向的进行偏移
        search_row = row - row_off;
        search_col = col - col_off;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            // 同⾊棋⼦数量++
            count++;
            // 检索位置继续向后偏移
            search_row -= row_off;
            search_col -= col_off;
        }
        return (count >= 5);
    }
private:
    uint64_t _room_id;
    int _player_count;
    uint64_t _white_id;
    uint64_t _black_id;
    room_statu _room_statu;
    online_user* _online_user;
    user_table* _user_table;
    std::vector<std::vector<int>> _board; // 棋盘管理
};
using room_ptr = std::shared_ptr<room>;
class room_manage
{
public:
    room_manage(user_table* ut,online_user* ou)
    :_next_id(1),_user_table(ut),_online_user(ou)
    {
        DBG_LOG("对战房间初始化成功!");
    }
    ~room_manage()
    {
        DBG_LOG("对战房间销毁完成!");
    }
    /**
     * 为两个用户创建房间
     */
    room_ptr create_room(uint64_t uid1,uint64_t uid2)
    {
        //首先判断两个用户是否在游戏大厅
        if(_online_user->user_in_hall(uid1) == false || _online_user->user_in_hall(uid2) == false)
        {
            DBG_LOG("用户未进入进入游戏大厅,对战失败!");
            return room_ptr();
        }
        //把这两个用户添加到同一游戏房间
        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rp(new room(_next_id,_online_user,_user_table));
        {
            //给用户分配黑白棋，分数低的为黑棋
            Json::Value uid1_user;
            _user_table->select_user_by_id(uid1,uid1_user);
            uint64_t uid1_user_score = uid1_user["score"].asInt64();
            Json::Value uid2_user;
            _user_table->select_user_by_id(uid2,uid2_user);
            uint64_t uid2_user_score = uid2_user["score"].asInt64();
            if(uid1_user_score >= uid2_user_score)
            {
                rp->add_black_user(uid2);
                rp->add_white_user(uid1);
            }
            else if(uid1_user_score < uid2_user_score)
            {
                rp->add_black_user(uid1);
                rp->add_white_user(uid2);
            }
            //将用户和房间管理起来
            _rooms.insert(std::make_pair(_next_id,rp));
            _users.insert(std::make_pair(uid1,_next_id));
            _users.insert(std::make_pair(uid2,_next_id));
            _next_id++;
        }
        return rp;
    }
    //移除房间
    void remove_room(uint64_t rid)
    {
        room_ptr rp = get_room_by_rid(rid);
        if(rp.get() == nullptr) return;
        //有用户了先移除用户
        uint64_t uid1 = rp->get_black_user();
        uint64_t uid2 = rp->get_white_user();
        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        //再移除房间
        _rooms.erase(rid);
    }
    /**
     * 移除房间中某一用户
     * 主要使用在房间中一个用户或掉线后，自动移除零一用户
     */
    void remove_player(uint64_t uid)
    {
        room_ptr rp = get_room_by_uid(uid);
        if(rp.get() == nullptr) return;
        rp->handle_exit(uid);
        if(rp->get_player_count() == 0)
            remove_room(rp->get_room_id());
        return;
    }
    //通过房间id获取房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rooms.find(rid);
        if(it == _rooms.end())
            return room_ptr();
        return it->second;
    }
    //通过用户id获取房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _users.find(uid);
        if(it == _users.end())
            return room_ptr();
        uint64_t rid = it->second;
        /* 这里不能直接调用get_room_by_rid 因为会造成死锁*/
        auto itt = _rooms.find(rid);
        if(itt == _rooms.end())
            return room_ptr();
        return itt->second;
    }
private:
    uint64_t _next_id;//创建的房间id
    std::mutex _mutex;
    user_table* _user_table;
    online_user* _online_user;
    std::unordered_map<uint64_t,room_ptr> _rooms;//房间id和房间关系
    std::unordered_map<uint64_t,uint64_t> _users;//用户id和房间id关系
};